const nonePic = "";
var tplList = {};
var loops = [];
var tplDrawQueue = [];
var lazyLoadList = [];
var loadQueue = [];
var loaded = {};
var canAutoPlay = false;
var app = new PIXI.Application({ width: 800, height: 600 });
var loader = new PIXI.loaders.Loader();
app.view.oncontextmenu = function (e) { e.preventDefault(); };
document.body.appendChild(app.view);
app.ticker.add(delta => _loop(delta));
app.ticker.FPS = 30;
app.ticker.minFPS = 30;


window.onerror = function (message, source, lineno, colno, error) {
	if (message.indexOf('is not defined') !== -1 || message.indexOf('is not defined') !== -1) {
		//console.log(message, source, lineno, colno, error);
	} else {
		//console.error(message); // 如果不是未定义变量错误，则抛出其他错误  
	}
};

_open("tpl/loading.tpl");

function _open(src, fname) {
	let node = null;
	if (fname && window[fname].node) {
		node = window[fname].node;
	} else {
		let name = src.replace(".tpl", "").replace("/", "_DIR_");
		node = _box(name, _formatConfig({name : name}, app.stage));
		app.stage.addChild(node);
	}
	_tplFlow(src, node);
}

function _loop(delta) {
	loops.forEach(k => {
		if (window[k] && window[k].visible) {
			window[k].update(delta);
		}
	});
}

function _destroy(name) {
	console.log("_destroy", name);
	window[name].visible = false;
	if (window[name].node) {
		window[name].node.destroy();
	}
	delete window[name];
}

function _tplFlow(src, node = null) {
	let className = src.replace(".tpl", "").replace("/", "_DIR_");
	if (node && node.name) {
		className = node.name;
	}

	tplDrawQueue.push(className);

	if (!tplList[src]) {
		tplList[src] = _loadTpl(src);
	}

	if (!window[className]) {
		window[className] = _script(className, tplList[src].script);
	}

	window[className].src = src;
	window[className].node = node;
	window[className].parent = _findClassParent(node);
	window[className].childNodes = {};
	window[className].childVars = {};

	_listen(className);

	if (!window[className].template) {
		window[className].template = _template(className, tplList[src].template);
	}

	_preLoad(window[className].template, () => {
		window[className].readyDraw = true;
		_drawQueue();
		//console.log(app.stage.children);
	});
}

function _findClassParent(node) {
	let parentClass = null;
	if(node.parent){
		if(window[node.parent.name]){
			if(window[node.parent.name].readyDraw == undefined){
				let tempParent = _findClassParent(window[node.parent.name].node);
				if(tempParent){
					parentClass = tempParent;
				}
			}else{
				parentClass = window[node.parent.name];
			}
		}
	}
	return parentClass;
}

function _drawQueue() {
	for (let n = 0; n < tplDrawQueue.length; n++) {
		if (window[tplDrawQueue[n]].readyDraw) {
			window[tplDrawQueue[n]].readyDraw = false;
			let className = tplDrawQueue[n];
			_draw(className, window[className].template, window[className].node);
			loops.push(className);
			window[className].create();
			tplDrawQueue.splice(n, 1);
			n--;
		} else {
			break;
		}
	}
}

function _lazyLoad(srcs = [], callbacks = []) {

	let loadedNum = srcs.length;
	for (let n = 0; n < srcs.length; n++) {
		if (loader.resources[srcs[n]]) {
			srcs.splice(n, 1);
			n--;
			loadedNum--;
		} else if (loaded[srcs[n]]) {
			srcs.splice(n, 1);
			n--;
			loadedNum--;
		} else {
			loaded[srcs[n]] = true;
		}
	}

	if (loadedNum == 0 && callbacks.length > 0) {
		callbacks.forEach(element => {
			element();
		});
		return;
	}

	
	if (srcs.length > 0) {
		loadQueue.push({ srcs: srcs, callbacks: callbacks });
	}

	if (loader.loading || loadQueue.length == 0) {
		return;
	}

	let res = srcs;
	let cbs = callbacks;

	if (loadQueue.length > 0) {
		let first = loadQueue.shift();
		res = first.srcs;
		cbs = first.callbacks;
	}

	if (res.length > 0) {
		loader.add(res).load();
		loader.once('complete', () => {
			if (cbs.length > 0) {
				cbs.forEach(element => {
					element();
				});
			}
			_lazyLoad();
		});
	}
}

function _loadTpl(src) {
	let code = _get(src);
	return {
		script: code.match(/<script>([\S\s\t]*?)<\/script>/)[1],
		template: code.match(/<template>([\S\s\t]*?)<\/template>/)[0]
	};
}

function _script(className, scriptCode) {
	let classObj = null;
	eval(`class ${className}Class {
		constructor(){
		}
		openTpl(name, storey="${className}"){
			_open(name, storey);
		};
		jumpTpl(name){
			_open(name);
			this.destroy();
		};
		destroy(){
			this.destroyed();
			_destroy("${className}");
		};
		${scriptCode}
	};
	classObj = new ${className}Class();
	`);
	return classObj;
}

function _listen(className){
	let unCopy = ["child", "name", "src", "type", "absX", "absY", "visible", "onclick", "onrightclick", "ondown", "onup", "onmove", "onover", "onout"];
	let data = window[className].data();
	data.visible = true;
	data.x = 0;
	data.y = 0;
	data.width = 0;
	data.height = 0;
	if(window[className].node.type == "tpl"){//父模板传递初始变量+常量
		for (let k in window[className].node.config) {
			if(unCopy.indexOf(k) == -1){
				let isVar = false;
				if(window[className].parent.childVars[className]){//是变量
					window[className].parent.childVars[className].forEach(element => {
						if(element.k == k){
							isVar = true;
							data[k] = Object.assign((data[k] ? data[k] : {}), window[className].parent[element.v]);
						}
					});
				}
				if(isVar == false){
					data[k] = window[className].node.config[k];
				}
			}
		}
	}
	
	for (let k in data) {
		window[className][k] = data[k];
		_watch(window[className], k);
	}
}

function _watch(classObj, varName) {
	let value = classObj[varName];
	Object.defineProperty(classObj, varName, {
		enumerable: true,
		configurable: true,
		get() {
			return value;
		},
		set(newValue) {
			if (value === newValue || this.watchLock == true) {
				return;
			}
			if(this.node){
				this.node[varName] = newValue;//同步到节点
				if(this.node.type == "tpl"){//如果是模板
					if (varName == "visible" && newValue == true && this.visible == undefined) {//未加载tpl 动态加载
						value = newValue;
						let node = this.node;
						let src = this.src;
						delete this.visible;
						delete window[node.name];
						_tplFlow(src, node);
						return;
					}
					if(this.parent && this.parent.childVars && this.parent.childVars[this.node.name]){//同步到父类
						this.parent.childVars[this.node.name].forEach(element => {
							if(element.k == varName && this.parent[element.v] != newValue){
								//console.log("父:", this.node.name, element.k, "=>", newValue, element);
								this.watchLock = true;
								this.parent[element.v] = newValue;
								this.watchLock = false;
								//setTimeout(()=>{this.parent[element.v] = newValue;},1);
							}
						});
					}
					if(this.childVars){//同步到子类
						for (let k in this.childVars) {
							this.childVars[k].forEach(element => {
								if(element.v == varName && window[k][element.k] != newValue){
									//console.log("子:", k, element.k, "=>", newValue, element);
									this.watchLock = true;
									window[k][element.k] = newValue;
									this.watchLock = false;
									//setTimeout(()=>{window[k][element.k] = newValue;},1);
								}
							});
						}
					}
				}
			}
			_changeNodeVar(this.childNodes, this.childVars, varName, newValue);
			//console.log("set var", varName, newValue);
			if(this.watch && this.watch()[varName]){//模板监听变更
				this[this.watch()[varName]](value, newValue);
			}
			value = newValue;
		}
	});
	if (value && typeof value === 'object' && Object.keys(value).length > 0) {//监听子对象
		for (let k in value) {
			_watchChild(classObj, value, k, varName + "." + k);
		}
	}
}

function _watchChild(classObj, obj, varName, varChain = varName) {
	let value = obj[varName];
	Object.defineProperty(obj, varName, {
		enumerable: true,
		configurable: true,
		get() {
			return value;
		},
		set(newValue) {
			if (value === newValue) {
				return;
			}
			_changeNodeVar(classObj.childNodes, classObj.childVars, varChain, newValue);
			//console.log("_watchChild set var", varName, varChain, newValue);
			value = newValue;
		}
	});
	if (value && typeof value === 'object' && Object.keys(value).length > 0) {//多维监听子对象
		for (let k in value) {
			_watchChild(classObj, value, k, varChain + "." + k);
		}
	}
}

function _changeNodeVar(childNodes, childVars, varChain, newValue) {
	for (let k in childVars) {
		for (let n = 0; n < childVars[k].length; n++) {
			if (childVars[k][n].v == varChain) {
				if (!childNodes[k]) {
					break;
				}
				_changeNode(childNodes[k], childVars[k][n].k, newValue);
				break;
			}
		}
	}
}

function _changeNode(node, key, value) {
	if (key == "x") {
		node.x = parseInt(value);
	} else if (key == "y") {
		node.y = parseInt(value);
	} else if (key == "width") {
		node.width = parseInt(value);
	} else if (key == "height") {
		node.height = parseInt(value);
	} else if (key == "src") {
		if (node.type == "img") {
			_lazyLoad([value], [() => {
				let texture = loader.resources[value].texture;
				if (texture) {
					node.texture = texture;
				}
			}]);
		} else if (node.type == "ani") {
			_lazyLoad([value], [() => {
				let textures = loader.resources[value].textures;
				if (textures) {
					let frameArray = [];
					for (let k in textures) {
						frameArray.push(textures[k]);
					}
					node.textures = frameArray;
					node.width = node.config.width > 0 ? node.config.width : frameArray[0].width;
					node.height = node.config.height > 0 ? node.config.height : frameArray[0].height;
					node.currentFrame = 0;
					node.play();
				}
			}]);
		}
		//node.src = value;
	} else if (key == "text") {
		node.text = value;
	} else if (key == "size") {
		node.style.fontSize = parseInt(value);
	} else if (key == "color") {
		node.style.fill = value;
	} else if (key == "loop") {
		node.loop = Boolean(value);
	} else if (key == "speed") {
		node.animationSpeed = (value / 60).toFixed(2);
	} else if (key == "visible") {
		node.visible = Boolean(value);
	} else if (key == "index") {
		if(node.parent){
			let onIndex = -1;
			for (let index = 0; index < node.parent.children.length; index++) {
				if(node.parent.children[index].index <= value){
					onIndex++;
				}
			}
			node.parent.setChildIndex(node, onIndex);
		}
	} else if (key == "trimX") {
		node.trimX = parseInt(value);
	} else if (key == "trimY") {
		node.trimY = parseInt(value);
	}
}

function _template(className, templateCode) {
	let template = templateCode.match(/<template>([\S\s\t]*?)<\/template>/)[0];
	template = template.replace(/[\r\n]/g, "").replace(/\s+</g, "<").replace(/>\s+</g, ">");
	//template = template.replace(/<(\w+) /g, '<$1 name="' + name + '" ');
	template = template.match(/<template>([\S\s\t]*?)<\/template>/)[0];
	template = template.replace(/'/g, '"');
	template = template.replace("<template>", "[");
	template = template.replace("</template>", "]");
	template = template.replace(/<\/tpl>|<\/box>|<\/txt>|<\/ani>|<\/img>|<\/audio>/g, "},");
	template = template.replace(/\s+:(\w+)\s*="(.*?)"/g, ',"$1":"#v_a_r#$2#v-a-r#"');
	template = template.replace(/\s+@(\w+)\s*="(\w+)\((.*?)\)"/g, ',"on$1":"$2(#f_u_n_c#$3#f-u-n-c#)"');
	let funcs = template.match(/#f_u_n_c#(.*?)#f-u-n-c#/g);
	if(funcs){
		funcs.forEach(element => {
			template = template.replace(element, element.replace(/"/g, "'").replace(/#f_u_n_c#/g, "").replace(/#f-u-n-c#/g, ""));
		});
	}
	template = template.replace(/\s+@(\w+)\s*="(.*?)"/g, ',"on$1":"$2"');
	template = template.replace(/\s+(\w+)\s*="\[(.*?)\]"/g, ',"$1":[$2]');
	//template = template.replace(/\s+(\w+)\s*="\{(.*?)\}"/g, ',"$1":{$2}');
	template = template.replace(/\s+(\w+)\s*=/g, ',"$1":');
	template = template.replace(/></g, ',"child":[<');
	template = template.replace(/<tpl,/g, '{"type":"tpl",');
	template = template.replace(/<box,/g, '{"type":"box",');
	template = template.replace(/<txt,/g, '{"type":"txt",');
	template = template.replace(/<img,/g, '{"type":"img",');
	template = template.replace(/<ani,/g, '{"type":"ani",');
	template = template.replace(/<audio,/g, '{"type":"audio",');
	template = template.replace(/>/g, "");
	template = template.replace(/},},}/g, "}]}]}");
	template = template.replace(/},}/g, "}]}");
	template = template.replace(/},]$/g, "}]");
	//console.log(template);
	template = JSON.parse(template);
	template.forEach((element, key) => {//模板变量
		element = _templateVar(className, element, key);
	});
	return template;
}

function _templateVar(className, element, nameKey) {
	if (!element.name) {
		element.name = className + "_" + element.type + "_" + nameKey;
	}
	for (let k in element) {
		if (typeof element[k] !== 'string') {
			continue;
		}
		let vars = element[k].match(/#v_a_r#([\S\s]+?)#v-a-r#/g);
		if (vars) {//模板变量
			if (!window[className].childVars[element.name]) {
				window[className].childVars[element.name] = [];
			}
			for (let n = 0; n < vars.length; n++) {
				let varChain = vars[n].replace(/#v_a_r#|#v-a-r#/g, "");
				window[className].childVars[element.name].push({ k: k, v: varChain });
				let names = varChain.split(".");
				let defaultVar = window[className];
				for (let i = 0; i < names.length; i++) {
					defaultVar = defaultVar[names[i]];
					if (defaultVar === undefined) {
						console.error(names[i], "is not defined, on tpl: " + className);
						defaultVar = "";
					}
				}
				element[k] = element[k].replace(vars[n], defaultVar);
			}
		}
		if (element.child) {
			element.child.forEach((childElement, childKey) => {
				childElement = _templateVar(className, childElement, nameKey + "_" + childKey);
			});
		}
	}
	return element;
}

function _preLoad(template, callback = null) {
	let loadRes = _formatRes(template);
	if (loadRes.length > 0) {
		_lazyLoad(loadRes, [callback]);
	} else {
		if (callback) {
			callback();
		}
	}
}

function _formatRes(template, loadRes = []) {
	template.forEach(element => {
		if (element.src && element.type != "box") {
			if (!loader.resources[element.src] && loadRes.indexOf(element.src) == -1) {
				loadRes.push(element.src);
			}
		}
		if (element.child) {
			loadRes = _formatRes(element.child, loadRes);
		}
	});
	return loadRes;
}

function _draw(className, template, fnode = null) {
	template.forEach(element => {
		element = _formatConfig(element, fnode);
		let node = null;
		if (element.type == "tpl") {
			node = _tpl(className, element);
			if (element.src && element.child) {//记录异步渲染层次
				node.asynChildLength = element.child.length;
			}
		} else if (element.type == "box") {
			node = _box(className, element);
		} else if (element.type == "ani") {
			node = _ani(className, element);
		} else if (element.type == "img") {
			node = _img(className, element);
		} else if (element.type == "txt") {
			node = _txt(className, element);
		} else if (element.type == "audio") {
			node = _audio(className, element);
		}

		if (node) {
			if (window[className].childNodes) {
				window[className].childNodes[element.name] = node;
			}
			if (fnode && element.type != "audio") {
				if(fnode.asynChildLength && fnode.children.length >= fnode.asynChildLength){//调整异步渲染层次
					fnode.addChildAt(node, fnode.children.length - fnode.asynChildLength);
				}else{
					fnode.addChild(node);
				}
			}
		}

		if (element.type == "tpl" && element.visible === true) {
			_tplFlow(element.src, node);
		} else {//初始化时 不加载 只监听 visible
			window[element.name] = { src: element.src, node: node };
			_watch(window[element.name], "visible");
		}

		if (element.child) {
			_draw(className, element.child, node);
		}
	});
}

function _formatConfig(config, fnode = null) {
	if (config.type != "audio") {
		config.x = config.x ? parseInt(config.x) : 0;
		config.y = config.y ? parseInt(config.y) : 0;
		if (fnode) {//fnode.parent = null  顶级 app.stage
			config.absX = fnode.x + config.x;
			config.absY = fnode.y + config.y;
			if (config.x < 0) {
				config.x = app.screen.width + config.x;
			}
			if (config.y < 0) {
				config.y = app.screen.height + config.y;
			}
		}
		config.width = config.width ? parseInt(config.width) : 0;
		config.height = config.height ? parseInt(config.height) : 0;
		if (config.visible) {
			config.visible = (config.visible == "true") ? true : false;
		} else {
			config.visible = true;
		}
		config.index = config.index ? parseInt(config.index) : 0;
	}

	if (config.type == "img") {
		config.src = config.src ? config.src : "";
		config.radius = config.radius ? parseInt(config.radius) : 0;
		config.trimX = config.trimX ? parseInt(config.trimX) : 0;
		config.trimY = config.trimY ? parseInt(config.trimY) : 0;
	} else if (config.type == "box") {
		config.interactive = config.interactive ? (config.interactive == "true" ? true : false) : false;
	} else if (config.type == "ani") {
		config.src = config.src ? config.src : "";
		config.speed = config.speed ? parseInt(config.speed) : 10;
		config.loop = config.loop ? (config.loop == "true" ? true : false) : false;
		config.begin = config.begin ? parseInt(config.begin) : 0;
		if (config.end) {
			config.end = parseInt(config.end);
		}
	} else if (config.type == "txt") {
		config.text = config.text ? config.text : "";
		config.size = config.size ? parseInt(config.size) : 14;
		config.color = config.color ? config.color : "#000000";
	} else if (config.type == "audio") {
		config.src = config.src ? config.src : "";
		config.loop = config.loop ? (config.loop == "true" ? true : false) : false;
		config.play = config.play ? (config.play == "true" ? true : false) : false;
		config.volume = config.volume ? parseInt(config.volume > 1 ? 1 : config.volume) : 0.5;
	}

	return config;
}

function _tpl(className, config) {
	let tpl = new PIXI.Container();
	tpl.config = config;
	tpl.name = config.name;
	tpl.index = config.index;
	tpl.type = config.type;
	tpl.x = config.x;
	tpl.y = config.y;
	tpl.visible = config.visible;
	tpl.interactive = true;
	_addInputEvent(className, tpl, config);
	return tpl;
}

function _box(className, config) {
	let box = new PIXI.Container();
	box.config = config;
	box.name = config.name;
	box.index = config.index;
	box.type = config.type;
	box.x = config.x;
	box.y = config.y;
	box.visible = config.visible;
	box.interactive = config.interactive;
	if (config.filter) {
		let filter = new PIXI.filters.ColorMatrixFilter();
		let red = parseInt(config.filter.substring(1, 3), 16) / 255;
		let yellow = parseInt(config.filter.substring(3, 5), 16) / 255;
		let blue = parseInt(config.filter.substring(5, 7), 16) / 255;
		filter.matrix = [
			red, 0, 0, 0, 0,
			yellow, 0, 0, 0, 0,
			blue, 0, 0, 0, 0,
			0, 0, 0, 1, 0,
		];
		box.filters = [filter];
	}
	_addInputEvent(className, box, config);
	return box;
}

function _img(className, config) {
	let texture = null;
	if (loader.resources[config.src] && loader.resources[config.src].texture) {
		texture = loader.resources[config.src].texture;
	} else {
		texture = PIXI.Texture.from(nonePic);
	}

	let sprite = new PIXI.Sprite(texture);
	sprite.config = config;
	sprite.name = config.name;
	sprite.index = config.index;
	sprite.type = config.type;
	sprite.x = config.x;
	sprite.y = config.y;
	if(config.width == 0){
		config.width = texture.width;
	}
	if(config.height == 0){
		config.height = texture.height;
	}
	sprite.width = config.width;
	sprite.height = config.height;
	sprite.visible = config.visible;
	if (config.radius) {
		let graphics = new PIXI.Graphics();
		graphics.beginFill(0x333333);
		graphics.drawRoundedRect(config.absX, config.absY, sprite.width, sprite.height, config.radius);//x,y,width,height,radius
		graphics.endFill();
		sprite.mask = graphics;
	}
	if (config.onbind) {
		_bindNode(className, sprite, config.onbind);
	}
	if (config.trimX > 0 || config.trimY > 0) {
		sprite.texture = trimTexture();
		sprite.width -= config.trimX;
		sprite.x += config.trimX;
		sprite.height -= config.trimY;
		sprite.y += config.trimY;
		Object.defineProperty(sprite, "trimX", {
			enumerable: true,
			configurable: true,
			get() {
				return config.trimX;
			},
			set(newValue) {
				if (config.trimX === newValue) {
					return;
				}
				config.trimX = newValue;
				this.texture = trimTexture();
				this.scale = {x:1, y:1};
				this.x = config.x + config.trimX;
			}
		});
		Object.defineProperty(sprite, "trimY", {
			enumerable: true,
			configurable: true,
			get() {
				return config.trimY;
			},
			set(newValue) {
				if (config.trimY === newValue) {
					return;
				}
				config.trimY = newValue;
				this.texture = trimTexture();
				this.scale = {x:1, y:1};
				this.y = config.y + config.trimY;
			}
		});
	}
	if(config.grid){
		sprite.texture = gridTexture();
		Object.defineProperty(sprite, "width", {
			enumerable: true,
			configurable: true,
			get() {
				return config.width;
			},
			set(newValue) {
				if (config.width === newValue) {
					return;
				}
				config.width = newValue;
				this.width = newValue;
				this.texture = gridTexture();
				this.scale = {x:1, y:1};
			}
		});
		Object.defineProperty(sprite, "height", {
			enumerable: true,
			configurable: true,
			get() {
				return config.height;
			},
			set(newValue) {
				if (config.height === newValue) {
					return;
				}
				config.height = newValue;
				this.height = newValue;
				this.texture = gridTexture();
				this.scale = {x:1, y:1};
			}
		});
	}
	function trimTexture() {
		return new PIXI.Texture(texture.baseTexture, new PIXI.Rectangle(config.trimX, config.trimY, config.width - config.trimX, config.height - config.trimY));
	}
	function gridTexture() {
		let grids = config.grid.split(",").map(item => parseInt(item));//上 右 下 左
		let gridConfig = [
			{
				tLeft: grids[3], tTop: grids[0], tWith: texture.width - grids[1] - grids[3], tHeight: texture.height - grids[0] - grids[2],
				sX: grids[3], sY: grids[0], sWidth: config.width - grids[1] - grids[3], sHeight: config.height - grids[0] - grids[2]
			},//中间
			{
				tLeft: 0,tTop: 0,tWith: grids[3],tHeight: grids[0],
				sX: 0,sY: 0,sWidth: grids[3],sHeight: grids[0]
			},//左上
			{
				tLeft: grids[3],tTop: 0,tWith: texture.width - grids[1] - grids[3],tHeight: grids[0],
				sX: grids[3],sY: 0,sWidth: config.width - grids[1] - grids[3],sHeight: grids[0]
			},//上
			{
				tLeft: texture.width - grids[1],tTop: 0,tWith: grids[1],tHeight: grids[0],
				sX: config.width - grids[1],sY: 0,sWidth: grids[1],sHeight: grids[0]
			},//右上
			{
				tLeft: texture.width - grids[1],tTop: grids[0],tWith: grids[1],tHeight: texture.height - grids[0] - grids[2],
				sX: config.width - grids[1],sY: grids[0],sWidth: grids[1],sHeight: config.height - grids[0] - grids[2]
			},//右
			{
				tLeft: texture.width - grids[1],tTop: texture.height - grids[2],tWith: grids[1],tHeight: grids[2],
				sX: config.width - grids[1],sY: config.height - grids[2],sWidth: grids[1],sHeight: grids[2]
			},//右下
			{
				tLeft: grids[3],tTop: texture.height - grids[2],tWith: texture.width - grids[1] - grids[3],tHeight: grids[2],
				sX: grids[3],sY: config.height - grids[2],sWidth: config.width - grids[1] - grids[3],sHeight: grids[2]
			},//下
			{
				tLeft: 0,tTop: texture.height - grids[2],tWith: grids[3],tHeight: grids[2],
				sX: 0,sY: config.height - grids[2],sWidth: grids[3],sHeight: grids[2]
			},//左下
			{
				tLeft: 0,tTop: grids[0],tWith: grids[3],tHeight: texture.height - grids[0] - grids[2],
				sX: 0,sY: grids[0],sWidth: grids[3],sHeight: config.height - grids[0] - grids[2]
			}//左
		];
		let tempContainer = new PIXI.Container();
		let renderTexture = new PIXI.RenderTexture.create(config.width, config.height);
		let tempSprite = null;
		let tempTexture = null;
		gridConfig.forEach(element => {
			tempTexture = new PIXI.Texture(texture.baseTexture, new PIXI.Rectangle(element.tLeft, element.tTop, element.tWith, element.tHeight));
			tempSprite = new PIXI.Sprite(tempTexture);
			tempSprite.x = element.sX;
			tempSprite.y = element.sY;
			tempSprite.width = element.sWidth;
			tempSprite.height = element.sHeight;
			tempContainer.addChild(tempSprite);
		});
		app.renderer.render(tempContainer, renderTexture);
		tempSprite.destroy();
		tempTexture.destroy();
		tempContainer.destroy();
		return renderTexture;
	}
	_addInputEvent(className, sprite, config);
	return sprite;
}

function _ani(className, config) {
	let textures = [];
	if (loader.resources[config.src] && loader.resources[config.src].textures) {
		textures = loader.resources[config.src].textures;
	} else {
		let texture = PIXI.Texture.from(nonePic);
		for (let n = 0; n < 5; n++) {
			textures.push(texture);
		}
	}
	let frameArray = [];
	let n = 0;
	for (let k in textures) {
		if (n < config.begin || n > config.end) {
			n++;
			continue;
		}
		frameArray.push(textures[k]);
		n++;
	}
	let animatedSprite = new PIXI.extras.AnimatedSprite(frameArray);
	animatedSprite.config = config;
	animatedSprite.name = config.name;
	animatedSprite.index = config.index;
	animatedSprite.type = config.type;
	animatedSprite.x = config.x;
	animatedSprite.y = config.y;
	animatedSprite.width = config.width > 0 ? config.width : animatedSprite.width;
	animatedSprite.height = config.height > 0 ? config.height : animatedSprite.height;
	animatedSprite.animationSpeed = (config.speed / 60).toFixed(2);
	animatedSprite.loop = config.loop;
	animatedSprite.begin = config.begin;
	animatedSprite.end = config.end;
	animatedSprite.visible = config.visible;
	animatedSprite.play();
	if (config.onbind) {
		_bindNode(className, animatedSprite, config.onbind);
	}
	if (config.oncomplete) {
		animatedSprite.onFrameChange = () => {
			if (animatedSprite._currentTime > 0 && animatedSprite.currentFrame == 0) {
				_inputEvent(className, null, config.oncomplete);
			}
		}
	}
	_addInputEvent(className, animatedSprite, config);
	return animatedSprite;
}

function _txt(className, config) {
	let style = new PIXI.TextStyle({ fontFamily: "Arial", fontSize: config.size, fill: config.color, stroke: "#000000", strokeThickness: 2});
	let txt = new PIXI.Text(config.text, style);
	txt.config = config;
	txt.name = config.name;
	txt.index = config.index;
	txt.type = config.type;
	txt.x = config.x;
	txt.y = config.y;
	txt.visible = config.visible;
	if (config.onbind) {
		_bindNode(className, txt, config.onbind);
	}
	_addInputEvent(className, txt, config);
	return txt;
}

function _audio(className, config) {
	let audio = new Audio(config.src);
	audio.config = config;
	audio.name = config.name;
	audio.index = config.index;
	audio.type = config.type;
	audio.loop = config.loop;
	audio.volume = config.volume;
	if (config.play == true) {
		if (canAutoPlay) {
			audio.play();
		} else {
			document.querySelector("canvas").onclick = () => {
				if (!canAutoPlay) {
					canAutoPlay = true;
					audio.play();
				}
				document.querySelector("canvas").onclick = null;
			};
		}
	}
	if (config.onbind) {
		_bindNode(className, audio, config.onbind);
	}
	return audio;
}

function _bindNode(className, node, onbind){
	let nameArr = onbind.split(".");
	if (nameArr.length == 3) {
		window[className][nameArr[0]][nameArr[1]][nameArr[2]] = node;
	} else if (nameArr.length == 2) {
		window[className][nameArr[0]][nameArr[1]] = node;
	} else {
		window[className][nameArr[0]] = node;
	}
}

function _addInputEvent(className, node, config) {
	if (config.onclick) {
		node.interactive = true;
		node.on("click", (event) => { _inputEvent(className, event, config.onclick) });
		node.on("tap", (event) => { _inputEvent(className, event, config.onclick) });
	}
	if (config.onrightclick) {
		node.interactive = true;
		node.on("rightclick", (event) => { _inputEvent(className, event, config.onrightclick) });
	}
	if (config.ondown) {
		node.interactive = true;
		node.on("pointerdown", (event) => { _inputEvent(className, event, config.ondown) });
	}
	if (config.onup) {
		node.interactive = true;
		node.on("pointerup", (event) => { _inputEvent(className, event, config.onup) });
	}
	if (config.onmove) {
		node.interactive = true;
		node.on("pointermove", (event) => { _inputEvent(className, event, config.onmove) });
	}
	if (config.onover) {
		node.interactive = true;
		node.on("pointerover", (event) => { _inputEvent(className, event, config.onover) });
	}
	if (config.onout) {
		node.interactive = true;
		node.on("pointerout", (event) => { _inputEvent(className, event, config.onout) });
	}
}

function _inputEvent(className, event, func) {
	let param = [];
	let xy = "";
	if (event) {
		xy = {
			x: event.data.global.x,
			y: event.data.global.y
		};
	}

	let matchFunc = func.match(/(.*)\((.*)\)/);
	if (matchFunc) {
		func = matchFunc[1];
		param = matchFunc[2].replace(/"/g, "'").split(",");
		for (let n = 0; n < param.length; n++) {
			if (param[n] == "null") {
				param[n] = null;
			} else if (param[n][0] == "'") {
				param[n] = param[n].replace(/'/g, "");
			} else if (param[n] == "") {
				param.splice(n, 1);
				n--;
			} else if (param[n] == parseInt(param[n])){
				param[n] = parseInt(param[n]);
			} else if (param[n] == "$event"){
				param[n] = xy;
			}
		}
	}

	if (!param || param.length == 0) {
		window[className][func](xy);
	} else if (param.length > 0) {
		window[className][func](...param);
	}
}

function _get(url) {
	let httpRequest = new XMLHttpRequest();
	httpRequest.open("GET", url, false);
	httpRequest.setRequestHeader("Cache-Control", "no-cache");
	httpRequest.send();
	return httpRequest.responseText;
}

function G_copy(target) {
	if (typeof target === "object" && target !== null) {
		//判断为引用类型且不为null
		let cloneTarget = target instanceof Array ? [] : {};
		for (let item in target) {
			//筛出可枚举属性
			if (target.hasOwnProperty(item))
				//筛出自有属性 排除继承属性
				cloneTarget[item] = target[item];
		}
		return cloneTarget;
	} else {
		//源数据是基本类型数据，直接返回
		return target;
	}
}